package service

import (
	"context"
	"errors"
	"gitee.com/chicken-c/go-learn/webook/internal/domain"
	"gitee.com/chicken-c/go-learn/webook/internal/repostiory"
	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/bcrypt"
)

var (
	//创建用户重复的错误
	ErrUserDuplicate         = repostiory.ErrUserDuplicateEmail
	ErrInvalidUserOrPassword = errors.New("邮箱或密码不正确")
	ErrUserNotFound          = errors.New("用户不存在")
)

type UserService interface {
	SignUp(ctx context.Context, u domain.User) error
	Login(ctx context.Context, email string, password string) (domain.User, error)
	Edit(ctx context.Context, u domain.User) error
	Profile(ctx context.Context, userId int64) (domain.User, error)
	FindOrCreate(ctx *gin.Context, phone string) (domain.User, error)
	FindOrCreateByWechat(ctx *gin.Context, info domain.WechatInfo) (domain.User, error)
}

type userService struct {
	repo repostiory.UserRepository
}

func NewuserService(repo repostiory.UserRepository) UserService {
	return &userService{
		repo: repo,
	}
}

func (srv *userService) SignUp(ctx context.Context, u domain.User) error {
	hash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	u.Password = string(hash)
	return srv.repo.Create(ctx, u)
}

func (srv *userService) Login(ctx context.Context, email string, password string) (domain.User, error) {
	u, err := srv.repo.FindByEmail(ctx, email)
	if err == repostiory.ErrUserNotFound {
		//邮箱不存在的情况
		return domain.User{}, ErrInvalidUserOrPassword
	}
	if err != nil {
		//系统错误
		return domain.User{}, err
	}

	err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
	if err != nil {
		//密码错误的情况
		return u, ErrInvalidUserOrPassword
	}

	return u, nil
}

func (srv *userService) Edit(ctx context.Context, u domain.User) error {
	err := srv.repo.Modify(ctx, u)
	return err
}

func (srv *userService) Profile(ctx context.Context, userId int64) (domain.User, error) {
	ue, err := srv.repo.FindById(ctx, userId)
	if err == repostiory.ErrUserNotFound {
		//这种情况不可能存在，我们有middleware做登录校验
		return domain.User{}, ErrUserNotFound
	}

	return ue, err
}

func (srv *userService) FindOrCreate(ctx *gin.Context, phone string) (domain.User, error) {
	//先去找用户信息存不存在
	ue, err := srv.repo.FindByPhone(ctx, phone)
	if err != repostiory.ErrUserNotFound {
		//这里就是存在 或者 系统错误的情况
		return ue, err
	}

	//不存在我们就创建
	err = srv.repo.Create(ctx, domain.User{Phone: phone})
	if err != nil && err != ErrUserDuplicate {
		//创建时出现错误，但又不是唯一索引冲突，那就是系统错误
		return domain.User{}, err
	}

	//查询DB返回，但是这里需要考虑主从同步的延时问题
	return srv.repo.FindByPhone(ctx, phone)
}

func (srv *userService) FindOrCreateByWechat(ctx *gin.Context, info domain.WechatInfo) (domain.User, error) {
	//先去找用户信息存不存在
	ue, err := srv.repo.FindByWechat(ctx, info.OpenId)
	if err != repostiory.ErrUserNotFound {
		//这里就是存在 或者 系统错误的情况
		return ue, err
	}

	//不存在我们就创建
	err = srv.repo.Create(ctx, domain.User{WechatInfo: info})
	if err != nil && err != ErrUserDuplicate {
		//创建时出现错误，但又不是唯一索引冲突，那就是系统错误
		return domain.User{}, err
	}

	//查询DB返回，但是这里需要考虑主从同步的延时问题
	return srv.repo.FindByWechat(ctx, info.OpenId)
}
